#ifndef SPL_MSGSTRUCT
#define SPL_MSGSTRUCT

enum Orientation { Horizontal, Vertical };

struct CallbackHeader
{
  PBOOL handled;

  SPLINL CallbackHeader(PBOOL iHandled)
  {
    handled = iHandled;
  }
};
struct CallbackResultHeader : CallbackHeader
{
  LRESULT* result;

  SPLINL CallbackResultHeader(LRESULT* lResult, PBOOL iHandled)
    : CallbackHeader(iHandled)
  {
    result = lResult;
  }
};

struct PointHeader
{
  union
  {
    struct
    {
      //! Horizontal coordinate
      LONG x;
      //! Vertical coordinate
      LONG y;
    };

    //! Coordinates
    POINT point;
  };
};

struct SizeHeader
{
  union
  {
    struct
    {
      //! Horizontal size
      LONG width;
      //! Vertical size
      LONG height;
    };
    struct
    {
      //! Horizontal size
      LONG cx;
      //! Vertical size
      LONG cy;
    };

    //! Dimensions
    SIZE size;
  };
};

struct HitTestEvent : PointHeader
{
  // hit test value
  INT hitValue;

  SPLINL HitTestEvent(INT iHitValue, INT iX, INT iY)
  {
    hitValue = iHitValue;
    x = iX;
    y = iY;
  }
  SPLINL HitTestEvent(WPARAM wParam, LPARAM lParam)
  {
    hitValue = (INT)wParam;
    x = GET_X_LPARAM(lParam);
    y = GET_Y_LPARAM(lParam);
  }
};

struct DrawItemEvent : CallbackHeader
{
  WORD  id;
  PDRAWITEMSTRUCT data;

  SPLINL DrawItemEvent(WPARAM wParam, LPARAM lParam, PBOOL iHandled)
    : CallbackHeader(iHandled)
  {
    id    = (WORD)wParam;
    data  = reinterpret_cast<PDRAWITEMSTRUCT>(lParam);
  }
};

struct MeasureItemEvent : CallbackHeader
{
  WORD  id;
  PMEASUREITEMSTRUCT  data;

  SPLINL MeasureItemEvent(WPARAM wParam, LPARAM lParam, PBOOL iHandled)
    : CallbackHeader(iHandled)
  {
    id    = (WORD)wParam;
    data  = reinterpret_cast<PMEASUREITEMSTRUCT>(lParam);
  }
};

struct CompareItemEvent : CallbackResultHeader
{
  WORD  id;
  PCOMPAREITEMSTRUCT  data;

  SPLINL CompareItemEvent(WPARAM wParam, LPARAM lParam, LRESULT* lResult, PBOOL iHandled)
    : CallbackResultHeader(lResult, iHandled)
  {
    id    = (WORD)wParam;
    data  = reinterpret_cast<PCOMPAREITEMSTRUCT>(lParam);
  }
};

struct DeviceChangeEvent
{
  WORD  event;
  PDEV_BROADCAST_HDR  header;

  SPLINL DeviceChangeEvent(WPARAM wParam, LPARAM lParam)
  {
    event   = (WORD)wParam;
    header  = reinterpret_cast<PDEV_BROADCAST_HDR>(lParam);
  }
};

struct CommandEvent
{
  WORD  code;
  WORD  id;
  HWND  control;

  SPLINL CommandEvent(WPARAM wParam, LPARAM lParam)
  {
    code    = HIWORD(wParam);
    id      = LOWORD(wParam);
    control = reinterpret_cast<HWND>(lParam);
  }
};

struct ContextMenuEvent : PointHeader
{
  HWND    window;

  SPLINL ContextMenuEvent(WPARAM wParam, LPARAM lParam)
  {
    window  = reinterpret_cast<HWND>(wParam);
    point   = Geom::Point(lParam);
  }
};

struct MenuCommandEvent
{
  WPARAM  index;
  HANDLE  menu;

  SPLINL MenuCommandEvent(WPARAM wParam, LPARAM lParam)
  {
    index = wParam;
    menu  = reinterpret_cast<HANDLE>(lParam);
  }
};

struct DisplayChangeEvent
{
  UINT_PTR  bitsPerPixel;
  PSIZE     size;

  SPLINL DisplayChangeEvent(WPARAM wParam, LPARAM lParam)
  {
    bitsPerPixel  = (UINT_PTR)wParam;
    size          = reinterpret_cast<PSIZE>(lParam);
  }
};

struct EraseBackgroundEvent : CallbackHeader
{
  HDC   device;

  SPLINL EraseBackgroundEvent(WPARAM wParam, PBOOL iHandled)
    : CallbackHeader(iHandled)
  {
    device = reinterpret_cast<HDC>(wParam);
  }
};

struct NCPaintEvent : CallbackHeader
{
  HANDLE updateRegion;

  SPLINL NCPaintEvent(WPARAM wParam, PBOOL iHandled)
    : CallbackHeader(iHandled)
  {
    updateRegion  = reinterpret_cast<HANDLE>(wParam);
  }
};

struct PrintEvent
{
  HANDLE    device;
  LONG_PTR  options;

  SPLINL PrintEvent(WPARAM wParam, LPARAM lParam)
  {
    device  = reinterpret_cast<HANDLE>(wParam);
    options = (LONG_PTR)lParam;
  }
};

struct SetRedrawEvent
{
  BOOL  redraw;

  SPLINL SetRedrawEvent(WPARAM wParam)
  {
    redraw = (BOOL)wParam;
  }
};

struct ActivateEvent
{
  INT   windowState;
  BOOL  minimized;
  HWND  window;

  SPLINL ActivateEvent(WPARAM wParam, LPARAM lParam)
  {
    windowState = (INT)LOWORD(wParam);
    minimized   = (BOOL)HIWORD(wParam);
    window      = reinterpret_cast<HWND>(lParam);
  }
};

struct FocusEvent
{
  HWND  window;

  SPLINL FocusEvent(WPARAM wParam)
  {
    window  = reinterpret_cast<HWND>(wParam);
  }
};

struct HotkeyEvent
{
  INT   id;
  UINT  modifiers;
  UINT  vkCode;

  SPLINL HotkeyEvent(WPARAM wParam, LPARAM lParam)
  {
    id        = (INT)wParam;
    modifiers = LOWORD(lParam);
    vkCode    = HIWORD(lParam);
  }
};

struct KeyEvent
{
  // virtual key code
  BYTE vkCode;
  // autorepeat count when held down
  WORD repeatCount;
  // OEM scan code
  BYTE scanCode;
  // the key only appears on extended keyboards
  BOOL extended;
  // the alt key is down (system-key only)
  BOOL isAltDown;
  // the key was previously down
  BOOL wasDown;
  // the key is pressed
  BOOL isDown;

  SPLINL KeyEvent(WPARAM wParam, LPARAM lParam)
  {
    vkCode      = (BYTE)wParam;
    repeatCount = lParam & 0x0000FFFF;     /* 0-15  */
    scanCode    = lParam & 0x00FF0000 >> 16;  /* 16-23 */
    extended    = lParam & 0x1000000 >> 24;   /* 24    */
    /* 25-28 */
    isAltDown   = lParam & 0x20000000 >> 29; /* 29    */
    wasDown     = lParam & 0x40000000 >> 30;   /* 30    */
    isDown      = lParam & 0x80000000 >> 31;    /* 31    */
  }
};

struct InitDialogEvent
{
  HWND    control;

  union
  {
    PROPSHEETPAGE* page;
    LPARAM  data;
  };

  SPLINL InitDialogEvent(WPARAM wParam, LPARAM lParam)
  {
    control = (HWND)wParam;
    data    = lParam;
  }
};

struct MouseEvent : PointHeader
{
  SHORT mkButton;

  SPLINL MouseEvent(SHORT iMkButton, INT iX, INT iY)
  {
    mkButton = iMkButton;
    x = iX;
    y = iY;
  }
  SPLINL MouseEvent(WPARAM wParam, LPARAM lParam)
  {
    mkButton = GET_KEYSTATE_WPARAM(wParam);
    x = GET_X_LPARAM(lParam);
    y = GET_Y_LPARAM(lParam);
  }
};

struct MouseWheelEvent : MouseEvent
{
  SHORT       wheelDelta;
  Orientation wheelOrientation;

  SPLINL MouseWheelEvent(
    Orientation iWheelOrientation,
    WORD mkButton,
    SHORT iWheelDelta,
    INT x,
    INT y
    ) : MouseEvent(mkButton, x, y)
  {
    wheelDelta = iWheelDelta;
    wheelOrientation = iWheelOrientation;
  }
  SPLINL MouseWheelEvent(
    Orientation iWheelOrientation,
    WPARAM wParam,
    LPARAM lParam
    ) : MouseEvent(wParam, lParam)
  {
    wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam);
    wheelOrientation = iWheelOrientation;
  }
};

struct NCHitTestEvent : CallbackResultHeader
{
  POINT point;

  SPLINL NCHitTestEvent(LPARAM lParam, LRESULT* lResult, PBOOL iHandled)
    : CallbackResultHeader(lResult, iHandled)
  {
    point = Geom::Point(lParam);
  }
};

struct ScrollEvent
{
  // SB_THUMBPOSITION or SB_THUMBTRACK scroll position
  WORD  thumbPos;
  WORD  request;
  HWND  scrollbar;

  SPLINL ScrollEvent(WPARAM wParam, LPARAM lParam)
  {
    thumbPos  = HIWORD(wParam);
    request   = LOWORD(wParam);
    scrollbar = (HWND)lParam;
  }
};

struct ActivateAppEvent
{
  BOOL  activated;
  DWORD threadID;

  SPLINL ActivateAppEvent(WPARAM wParam, LPARAM lParam)
  {
    activated = (BOOL)wParam;
    threadID  = (DWORD)lParam;
  }
};

struct WndCreateEvent
{
  CREATESTRUCT* params;
  PBOOL create;

  SPLINL WndCreateEvent(LPARAM lParam, LRESULT* lResult)
  {
    params  = reinterpret_cast<CREATESTRUCT*>(lParam);
    create  = (PBOOL)lResult;
  }
};

struct EnableEvent
{
  BOOL  enabled;

  SPLINL EnableEvent(WPARAM wParam)
  {
    enabled = (BOOL)wParam;
  }
};

struct GetIconEvent : CallbackResultHeader
{
  UINT_PTR iconType;

  SPLINL GetIconEvent(WPARAM wParam, LRESULT* lResult, PBOOL iHandled)
    : CallbackResultHeader(lResult, iHandled)
  {
    iconType = (UINT_PTR)wParam;
  }
};

struct MinMaxInfoEvent : CallbackHeader
{
  PMINMAXINFO info;

  SPLINL MinMaxInfoEvent(LPARAM lParam, PBOOL iHandled)
    : CallbackHeader(iHandled)
  {
    info = reinterpret_cast<LPMINMAXINFO>(lParam);
  }
};

struct QueryEvent : CallbackResultHeader
{
  SPLINL QueryEvent(LRESULT* lResult, PBOOL iHandled)
    : CallbackResultHeader(lResult, iHandled)
  {
  }
};

struct QueryDragIconEvent : CallbackHeader
{
  PHANDLE icon;

  SPLINL QueryDragIconEvent(LRESULT* lResult, PBOOL iHandled)
    : CallbackHeader(iHandled)
  {
    icon = (PHANDLE)lResult;
  }
};

struct MoveEvent : PointHeader
{
  SPLINL MoveEvent(LPARAM lParam)
  {
    point = Geom::Point(lParam);
  }
};

struct MovingEvent
{
  PRECT   rect;

  SPLINL MovingEvent(LPARAM lParam)
  {
    rect = reinterpret_cast<PRECT>(lParam);
  }
};

struct NCCalcSizeEvent : CallbackResultHeader
{
  BOOL    update;

  union
  {
    NCCALCSIZE_PARAMS params;
    PRECT   client;
    LPARAM  param;
  };

  SPLINL NCCalcSizeEvent(WPARAM wParam, LPARAM lParam, LRESULT* lResult, PBOOL iHandled)
    : CallbackResultHeader(lResult, iHandled)
  {
    update  = (BOOL)wParam;
    param   = lParam;
  }
};

struct ParentNotifyEvent
{
  INT       msg;
  INT       data;
  LPARAM    param;

  SPLINL ParentNotifyEvent(WPARAM wParam, LPARAM lParam)
  {
    msg   = LOWORD(wParam);
    data  = HIWORD(wParam);
    param = lParam;
  }
};

struct ShowWindowEvent
{
  BOOL      shown;
  LONG_PTR  status;

  SPLINL ShowWindowEvent(WPARAM wParam, LPARAM lParam)
  {
    shown   = (BOOL)wParam;
    status  = (LONG_PTR)lParam;
  }
};

struct SizeEvent : SizeHeader
{
  UINT_PTR  sizeType;

  SPLINL SizeEvent(WPARAM wParam, LPARAM lParam)
  {
    sizeType  = (UINT_PTR)wParam;
    size      = Geom::Size(lParam);
  }
};

struct SizingEvent
{
  UINT_PTR  sizeEdge;
  PRECT     rect;

  SPLINL SizingEvent(WPARAM wParam, LPARAM lParam)
  {
    sizeEdge  = (UINT_PTR)wParam;
    rect      = reinterpret_cast<PRECT>(lParam);
  }
};

struct StyleEvent
{
  UINT_PTR  style;
  LPSTYLESTRUCT data;

  SPLINL StyleEvent(WPARAM wParam, LPARAM lParam)
  {
    style = (UINT_PTR)wParam;
    data  = reinterpret_cast<LPSTYLESTRUCT>(lParam);
  }
};

struct WindowPosEvent
{
  PWINDOWPOS  pos;

  SPLINL WindowPosEvent(LPARAM lParam)
  {
    pos = reinterpret_cast<PWINDOWPOS>(lParam);
  }
};

struct DragEvent
{
  DWORD         keyState;
  PPOINT        point;
  PDWORD        effect;

  SPLINL DragEvent(DWORD keyState, PPOINT point, PDWORD effect)
  {
    this->keyState  = keyState;
    this->point     = point;
    this->effect    = effect;
  }
};

struct DragObjectEvent : DragEvent
{
  IDataObject*  object;

  SPLINL DragObjectEvent(IDataObject* object, DWORD keyState, PPOINT point, PDWORD effect)
    : DragEvent(keyState, point, effect)
  {
    this->object = object;
  }
};

struct DragFeedbackEvent
{
  DWORD effect;

  SPLINL DragFeedbackEvent(DWORD effect)
  {
    this->effect = effect;
  }
};

struct QueryContinueDragEvent
{
  BOOL  escapePressed;
  DWORD keyState;

  SPLINL QueryContinueDragEvent(BOOL escapePressed, DWORD keyState)
  {
    this->escapePressed = escapePressed;
    this->keyState = keyState;
  }
};

struct WndProcEvent
{
  UINT      msg;
  WPARAM    wParam;
  LPARAM    lParam;
  LRESULT   lResult;
  BOOL      handled;
};

struct NotifyIconEvent
{
  UINT  id;
  WORD  msg;

  SPLINL NotifyIconEvent(WPARAM wParam, LPARAM lParam)
  {
    id  = (UINT)wParam;
    msg = (WORD)lParam;
  }
};

struct TimerEvent
{
  UINT  id;

  SPLINL TimerEvent(WPARAM wParam)
  {
    id = (UINT)wParam;
  }
};

struct NotifyEvent : CallbackResultHeader
{
  WORD    id;
  NMHDR*  data;

  SPLINL NotifyEvent(WPARAM wParam, LPARAM lParam, LRESULT* lResult, PBOOL iHandled)
    : CallbackResultHeader(lResult, iHandled)
  {
    id    = (WORD)wParam;
    data  = reinterpret_cast<NMHDR*>(lParam);
  }
};

#endif